/*
 * BB.java: Basketball Predictor assignment
 * 
 * This code defines a graph structure, loads an XML file of games,
 * and creates a GUI 
 */
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.TreeMap;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.WindowConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import ann.gui.CloseableFrame;

public class BB extends CloseableFrame{
	private static final long serialVersionUID = 1L;

	//use TreeMap instead of HashMap of teams to get them in sorted order
	static TreeMap<String,Team> teams = new TreeMap<String,Team>(); 	// teams
	static Vector<Game> games = new Vector<Game>();						// games

	JComboBox team1Combo = new JComboBox();
	JComboBox team2Combo = new JComboBox();
	JButton compareButton = new JButton("Compare");
	JButton loadXML = new JButton("Load XML File");
	JPanel buttonPanel = new JPanel();
	JTextArea textArea = new JTextArea("\n  Select teams and Compare!");

	/*
	 * Game and Team classes below
	 * Would make the external classes, 
	 * but when loading the big file I run out of memory
	 */
	public static class Game{

		Team team1;
		int  team1score;
		Team team2;
		int  team2score;
		String date;
		char label;		//U[nexplored], D[iscovery], C[ross], for traversal

		/*
		 * Game constructor: build the game and add it to each team's game list
		 * If the team doesn't yet exist, create it.
		 */
		Game(String T1, int T1score, String T2, int T2score, String gameDate) {
			if (!teams.containsKey(T1)) {  // create team node if necessary
				Team team1 = new Team(T1);
				teams.put(T1,team1);
			}
			if (!teams.containsKey(T2)) {
				Team team2 = new Team(T2);
				teams.put(T2,team2);
			}
			team1=teams.get(T1);
			team2=teams.get(T2);
			team1score=T1score;
			team2score=T2score;
			date = gameDate;

			if (team1score > team2score){		// add to team 1 win record
				team1.avgMov = 
					( (team1.avgMov * team1.wins) + (team1score - team2score) ) / 
					(team1.wins + 1); // Increment the average mov

				team1.wins++;
				team2.losses++;


			} else {							// add to team 2 win record
				team2.avgMov = 
					( (team2.avgMov * team2.wins) + (team2score - team1score) ) / 
					(team2.wins + 1);  //increment the average mov

				team1.losses++;
				team2.wins++;
			}

			team1.numGames++;
			team2.numGames++;

			team1.games.add(this);	// add this game to team's game list
			team2.games.add(this);

			label = 'U';				// set the initial label to unexplored

		}
	}


	public static class Team{
		String name;			// team name
		Vector<Game> games; 	// all games played
		int wins;				// number of wins
		int losses;				// number of losses
		int numGames;			// total number of games
		double avgMov;			// average margin of victory
		int totalWinPts;		// testing purposes
		Team parent = null;		// keeps track of the parent
		char tLabel;			// traversal label
		int movToParent;		// MOV to parent

		Team (String teamname) { 
			name=teamname; 
			wins = losses = numGames = totalWinPts = 0;
			avgMov = 0.0;
			games = new Vector<Game>();
		}
	}



	/*
	 * BB Constructor builds a simple GUI.
	 * Can you make it look nicer?  No, it looks fine :D
	 */
	public BB() {
		super("Derek Slenk's Probably Accurate Basketball Predictor");
		this.getContentPane().setLayout(new BorderLayout());

		// add team names to the two combo boxes
		for (String team: teams.keySet()) {				// for each team
			team1Combo.addItem(team);
			team2Combo.addItem(team);
		}

		// put the two combos and the button on a panel 
		buttonPanel.add(loadXML);
		buttonPanel.add(team1Combo);
		buttonPanel.add(team2Combo);
		buttonPanel.add(compareButton);

		java.awt.Container contentPane = getContentPane();
		contentPane.add(buttonPanel, BorderLayout.NORTH);
		contentPane.add(textArea, BorderLayout.CENTER);

		// set up the compareButton to call the compare() function
		compareButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) { compare(); }
		});
		
		// set up the load XML button to show the user a file picker
		loadXML.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				loadGames();
			}
		});
	}

	/*
	 * Called when compare is clicked
	 * Loads the teams from the combo-boxes and calls the appropriate functions
	 */
	@SuppressWarnings("unchecked")
	public void compare() {
		String team1 = (String) team1Combo.getSelectedItem();
		String team2 = (String) team2Combo.getSelectedItem();

		basicTeamInfo(team1, team2);	// Append the basic team info
		head2headInfo(team1, team2);	// Head-to-Head calculations
		getStrengths(team1, team2);		// Strength Calculations

		/*
		 * We were having problems with the BFS choosing A - C as the shortest
		 * path, when we wanted it to use A-B-C.  To do this, we temporarily
		 * remove the conflicting game from the tree and then add it back in 
		 * afterwards
		 */
		ArrayList<Game> headToHeadGames = removeHeadToHead(team1, team2);

		BFS(team1, team2);				// the Breadth-first-search to calculate the paths
		// between 2 teams

		/*
		 * Put the head to head games back in (see above as to why
		 * we removed it
		 */
		if (headToHeadGames.size() > 0) {
			replaceHeadToHead(headToHeadGames, team1);
		}

		/*
		 * When you are in the program, you want to be able to choose
		 * two different teams and run it again.  
		 * Problem is, we change the tags.  This resets all the tags
		 * to how they were
		 */
		java.util.Enumeration e = games.elements();
		for (int i = 0; i < games.size(); i++) {
			Game g = (Game) e.nextElement();
			// clear the parent team property
			g.team1.parent = null;
			g.team2.parent = null;
			// set all team labels to unvisited
			g.team1.tLabel = 'U';
			g.team2.tLabel = 'U';
		}

	}

	/**
	 * This function finds the basic information:
	 * win/loss record and mov for the two teams that 
	 * are passed in
	 * @param String t1name - name of the first team
	 * @param String t2name - name of the second team
	 * @author Derek Slenk
	 */
	private void basicTeamInfo(String t1name, String t2name){
		//Number formatting for the margin of victory
		NumberFormat nf = new DecimalFormat("00.00");
		String mov1 = nf.format(teams.get(t1name).avgMov);
		String mov2 = nf.format(teams.get(t2name).avgMov);

		//Start outputting information to the screen
		textArea.setText("Comparing ");
		textArea.append(t1name);
		textArea.append(" and ");
		textArea.append(t2name);
		textArea.append("\n");
		textArea.append(t1name + "\n");

		//Retrieves win/loss record from the Team class
		textArea.append("\t" + " Win/Loss Record: " + teams.get(t1name).wins + "/" + teams.get(t1name).losses);
		textArea.append("\n");

		//MOV from up above
		textArea.append("\t" + "Average MOV: " + mov1);
		textArea.append("\n");
		textArea.append(t2name + "\n");

		//Retrieves win/loss record form the Team class
		textArea.append("\t" + " Win/Loss Record: " + teams.get(t2name).wins + "/" + teams.get(t2name).losses);
		textArea.append("\n");

		//MOV from up above
		textArea.append("\t" + "Average MOV: " + mov2);
		textArea.append("\n");

		//Calls movWinner(), which just is a comparison with the full numbers
		textArea.append("Expected Winner based on MOV: " + movWinner(t1name, t2name));
		textArea.append("\n");

	}

	/**
	 * This takes the two teams, grabs their movs,
	 * calculates the winner, and returns a
	 * predertimined string
	 * @param t1name
	 * @param t2name
	 * @return a String, either the team name that won or a tie
	 */
	private static String movWinner(String t1name, String t2name){
		if (teams.get(t1name).avgMov > teams.get(t2name).avgMov){
			return t1name;	// if team1 won 
		}else if (teams.get(t2name).avgMov > teams.get(t1name).avgMov){
			return t2name;	// if team2 won
		}else{
			return "Tie";	// if they tied, or neither won
		}

	}

	/**
	 * This function gets the results of the two teams
	 * head-to-head match-up info and appends the information
	 * to the text area
	 * @param t1name
	 * @param t2name
	 */
	private void head2headInfo(String t1name, String t2name){
		// Initialize variables needed to calculate the head-to-head winner
		int team1h2hwins = 0;
		int team2h2hwins = 0;
		String teamh2hwinner;

		//Start appending text
		textArea.append("\n");
		textArea.append("Head-to-Head Competitions: ");
		textArea.append("\n");

		//Get a vector of team 1's games, and then load it into an enumerator (make it easy to compare)
		Vector<Game> teamGames = teams.get(t1name).games;
		java.util.Enumeration e = teamGames.elements();		
		while (e.hasMoreElements()) {				// for each game in the vector
			Game g = (Game) e.nextElement();		// Go to the next element in the game
			if (g.team1.name == teams.get(t2name).name || g.team2.name == teams.get(t2name).name ){
				textArea.append("    " + g.team1.name + " " + g.team1score);
				textArea.append(" "  + g.team2.name + " " + g.team2score);
				textArea.append(" -- " + g.date + "\n");

				/*
				 * These if statements increment the right team that won,
				 * because your 'team1' could be 'team1' in one game
				 * and 'team2' in another game (Home v Away)
				 */
				if (g.team1score > g.team2score && g.team1.name == t1name){
					team1h2hwins++;
				}else if (g.team1score > g.team2score && g.team1.name == t2name){
					team2h2hwins++;
				}else if (g.team2score > g.team1score && g.team2.name == t1name){
					team1h2hwins++;
				}else if (g.team2score > g.team1score && g.team2.name == t2name){
					team2h2hwins++;
				}else{
					System.out.println("They tied....");		// Just to handle a wierd case
				}
			}
		}

		/*
		 * Just checks to see which team has the more head-to-head wins
		 * Or tells you that it was not possible
		 */
		if (team1h2hwins > team2h2hwins){
			teamh2hwinner = t1name;
		}else if (team2h2hwins > team1h2hwins){
			teamh2hwinner = t2name;
		}else{
			teamh2hwinner = "No Determinable Winner";
		}
		textArea.append("Expected Winner based on Head-to-Head Games: " + teamh2hwinner + "\n");
	}

	/**
	 * This routine calculates a team's strength using that team's winning
	 * percentage and the average margin of victory. it is another approach at
	 * guessing the outcome of a game
	 * 
	 * @param t1name
	 *            - the name of team 1
	 * @param t2name
	 *            - the name of team 2
	 */
	private void getStrengths(String t1name, String t2name) {

		// initialize the string that will hold the strength report
		String report = "Strength Report:\n";

		// gather the three numbers that we will need from each team
		double team1Mov = teams.get(t1name).avgMov;
		double team1NumGames = teams.get(t1name).games.size();
		double team1Wins = teams.get(t1name).wins;
		double team2Mov = teams.get(t2name).avgMov;
		double team2NumGames = teams.get(t2name).games.size();
		double team2Wins = teams.get(t2name).wins;

		/*
		 * using the previous numbers, apply them to the strength algorithm to
		 * get a number that we can compare. the number comes from multiplying
		 * the team's winning percentage by it's average margin of victory.
		 * since average mov is only calculated from games that team has won,
		 * this gives a more equal representation, as a team that won only 1
		 * game - but won that game by 20 points - would have a great average
		 * mov. this way, the lousy winning percentage of that team will not go
		 * unnoticed
		 */
		double team1Strength = Math.floor((team1Wins / team1NumGames)
				* team1Mov);
		double team2Strength = Math.floor((team2Wins / team2NumGames)
				* team2Mov);

		// now add the results to the result string
		report += t1name + ": " + team1Strength + "  " + t2name + ": "
		+ team2Strength + "\n" + "Expected Winner based on Strength: ";

		// the team with the highest number here is expected to win
		if (team1Strength > team2Strength) {
			report += t1name + "\n";
		} else if (team1Strength < team2Strength) {
			report += t2name + "\n";

			// if the strength calculation for each team is the same, consider
			// the mov
			// to decide the expected winner
		} else {
			if (team1Mov > team2Mov) {
				report += t1name + "(their M.O.V. is higher)\n";
			} else if (team1Mov < team2Mov) {
				report += t2name + "(their M.O.V. is higher)\n";
			} else {
				report += "neither teams! It will likely be a draw\n."
					+ "Their Strength and M.O.V. are equal!\n";
			}
		}

		// add the results to the UF
		textArea.append("\n" + report);

	}

	/**
	 * This executes a breadth first search on the tree that is created
	 * from the matchups in the XML file.  This uses a queue data structure
	 * based off of a linked list.  It uses node and edge labels to mark
	 * where we have been and where we need to go.  Starts at the node of
	 * team1, and goes to team2.  Appends the trail that is greater than length 1
	 * 
	 * @param t1name
	 *            - the name of team 1
	 * @param t2name
	 *            - the name of team 2
	 */
	private void BFS(String t1name, String t2name) {

		// get the collection of team 1's games - now without any head to head
		// matchups with team 2
		Vector<Game> currentTeamGames = teams.get(t1name).games;

		// initilize queue that we will use to map the traversal
		Queue<Team> BFSqueue = new LinkedList<Team>();

		// this flag will be used to tell if we left the loop because no path
		// was
		// found or if a path was found
		boolean foundChain = true;

		// initialize some temp vars to use when traversing the tree
		Team tempTeam = null;
		Game tempGame = null;

		// start at team 1's node and label it visitied
		teams.get(t1name).tLabel = 'V';

		// insert team 1's node as the first item in the queue
		BFSqueue.offer(teams.get(t1name));

		// loop until the second team is found
		while (tempTeam != teams.get(t2name)) {

			// check if the queue is empty. if it is, we have visited each node
			// and not found a match so set the flag appropriately and leave the
			// loop
			if (BFSqueue.isEmpty()) {
				foundChain = false;
				break;
			}

			// remove the first node/team in the queue to visit it
			tempTeam = BFSqueue.poll();

			// get the games associated with that node (team)
			currentTeamGames = tempTeam.games;

			// initialize an enum so we can examine each game
			java.util.Enumeration e = currentTeamGames.elements();

			// examine all of this node's children
			for (int i = 0; i < tempTeam.games.size(); i++) {
				tempGame = (Game) e.nextElement();

				// if team 1 is the active team, add team 2 to the queue
				if (tempGame.team1 == tempTeam) {
					// if the opponent node has not been visited
					if (tempGame.team2.tLabel != 'V') {
						// add the "child" (opponent) to the queue
						BFSqueue.offer(tempGame.team2);
						// mark it as visited
						tempGame.team2.tLabel = 'V';
						// set the parent property that we will use later to
						// output the path
						tempGame.team2.parent = tempTeam;
						// keep track of the margin of victory of the games included
						// in the path. the outcome of the game will be determined by this
						tempGame.team2.movToParent = tempGame.team2score - tempGame.team1score;
					}

					// otherwise team 2 is the active team, so add team 1 to the queue
				} else if (tempGame.team2 == tempTeam) {
					if (tempGame.team1.tLabel != 'V') {
						BFSqueue.offer(tempGame.team1);
						tempGame.team1.tLabel = 'V';
						tempGame.team1.parent = tempTeam;
						tempGame.team1.movToParent = tempGame.team1score - tempGame.team2score;
					}
				}
			}
		}

		/*
		 * if we left the loop because we discovered the team 2 node, "unwind"
		 * from that node, appending each node's parent to the text area. this
		 * will yield a version of the shortest path from team 1 to team 2. it
		 * may not be the ONLY route possible (this accounts for different paths
		 * when the same teams are compared, just with their positions as team 1
		 * and 2 reversed) but there will not be a shorter one.
		 * 
		 * the expected score is calculated by summing up the margins of victory
		 * with respect to the parents for all of the teams included in the path
		 */
		if (foundChain == true) {
			// remember the name of the first team so we can list it along with
			// teh expected mov of the game described
			String firstTeam = tempTeam.name;
			textArea.append("\nThe shortest path is: " + tempTeam.name);
			// initialize the int that will hold the expected mov for the game
			// described by our shortest path
			int expectedMov = tempTeam.movToParent;
			while (tempTeam.parent != null) {
				tempTeam = tempTeam.parent;
				expectedMov += tempTeam.movToParent;
				textArea.append(" who played " + tempTeam.name);
			}
			// predict the mov for the game described by the path listed
			// it is based on the cumulative mov's for the games in the path
			textArea.append("\n  The expected margin of victory for " + firstTeam
					+ " vs. " + tempTeam.name + " is " + expectedMov + " points");

			// if foundChain is false, that means there was no path discovered,
			// so inform the
			// user on the UF
		} else {
			textArea.append("\nNo path discovered");
		}
	}

	/**
	 * This routine replaces the head to head games that were taken out so the
	 * breadth first search would not return any paths of length 1.
	 * 
	 * @param gamesToAdd
	 *            - an array of game objects to add back into team 1's
	 *            collection
	 * @param t1name
	 *            - the name of team 1
	 */
	private void replaceHeadToHead(ArrayList<Game> gamesToAdd, String t1name) {
		// add whatever is in the replacement array back into the team's game collection
		for (int j = 0; j < gamesToAdd.size(); j++) {
			Game g = gamesToAdd.get(j);
			teams.get(t1name).games.add(g);
		}

	}

	/**
	 * This function goes through team 1's games and removes
	 * and head-to-head matches with team 2.  We do this so when
	 * we do the breadth first search, we don't get matchups of 
	 * length 1
	 * 
	 * @param t1name
	 *            - the name of team 1
	 * @param t2name
	 *            - the name of team 2
	 * @return headToHeadGames - this is an array of the games that were
	 *         removed. we will insert them back into the vector after
	 *         calculating the shortest path
	 */
	private ArrayList<Game> removeHeadToHead(String t1name, String t2name) {

		// get all of team 1's games
		Vector<Game> teamGames = teams.get(t1name).games;

		// initialize the array to hold any games removed
		ArrayList<Game> headToHeadGames = new ArrayList<Game>();

		// define an enum so we can examine each of team 1's games
		java.util.Enumeration e2 = teamGames.elements();

		/*
		 * loop through every one of team 1's games, adding all head to head
		 * matchups to the array. NOTE: the games are not removed until the
		 * entire collection of games has been processed. if we remove them in
		 * this loop, the counter for the enum gets confused and some games will
		 * be skipped (i learned this the hard way).
		 */
		while (e2.hasMoreElements()) {

			// Temp variable for just a certain match
			Game g = (Game) e2.nextElement();
			
			// If head-to-head, add to array to remove
			if ((g.team1.name == t1name && g.team2.name == t2name)
					|| (g.team1.name == t2name && g.team2.name == t1name)) {
				headToHeadGames.add(g);
			}
		}

		// If any head-to-head found, remove them
		if (headToHeadGames.size() != 0) {
			for (int k = 0; k < headToHeadGames.size(); k++) {
				// remove the game
				teamGames.remove(headToHeadGames.get(k));
			}
		}

		// Returns the array of games so we can add them back in later
		return headToHeadGames;

	}

public void loadGames() {
		
		// show the user a file picker so he/she can select the XML file
		JFileChooser chooser = new JFileChooser();

		chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		int result = chooser.showOpenDialog(this);
		if (result == JFileChooser.CANCEL_OPTION)
			return;
		
		// get the file the user chose
		File gameFile = chooser.getSelectedFile();

		// code written by Prof. Plantinga to parse the XML file and build the teams
		// and games collections
		try {

			DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
			Document doc = docBuilder.parse(gameFile);
			doc.getDocumentElement().normalize();

			NodeList listOfGames = doc.getElementsByTagName("game");

			for (int g = 0; g < listOfGames.getLength(); g++) {
				Node gameNode = listOfGames.item(g);
				if (gameNode.getNodeType() == Node.ELEMENT_NODE) {
					Element gameElement = (Element) gameNode;
					String team1 = gameElement.getElementsByTagName("team1")
							.item(0).getTextContent();
					String team1Score = gameElement.getElementsByTagName(
							"team1Score").item(0).getTextContent();
					String team2 = gameElement.getElementsByTagName("team2")
							.item(0).getTextContent();
					String team2Score = gameElement.getElementsByTagName(
							"team2Score").item(0).getTextContent();
					String date = gameElement.getElementsByTagName("date")
							.item(0).getTextContent();
					games.add(new Game(team1, Integer.parseInt(team1Score),
							team2, Integer.parseInt(team2Score), date));
				}
			}

			// add team names to the two combo boxes on the UF
			for (String team : teams.keySet()) {
				team1Combo.addItem(team);
				team2Combo.addItem(team);
			}

		} catch (SAXParseException err) {
			System.out.println("** Parsing error" + ", line "
					+ err.getLineNumber() + ", uri " + err.getSystemId());
			System.out.println(" " + err.getMessage());
		} catch (SAXException e) {
			Exception x = e.getException();
			((x == null) ? e : x).printStackTrace();
		} catch (Throwable t) {
			t.printStackTrace();
		}

	}

	/*
	 * Load an XML file of games using Java's DOM parser. 
	 * 
	 * This function adds games to the games vector class variable; 
	 * the Game constructor adds them to the game list for each team.
	 */
	public static void loadGames(String gameFile) {
		System.out.println("In loadGames\n");
		try {
			DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
			Document doc = docBuilder.parse(new File(gameFile));
			doc.getDocumentElement().normalize();

			NodeList listOfGames = doc.getElementsByTagName("game");
			System.out.println("Total games: " + listOfGames.getLength());

			for (int g=0; g<listOfGames.getLength(); g++) {
				Node gameNode = listOfGames.item(g);
				if (gameNode.getNodeType() == Node.ELEMENT_NODE) {
					Element gameElement = (Element) gameNode;
					String team1 =      gameElement.getElementsByTagName("team1").item(0).getTextContent();
					String team1Score = gameElement.getElementsByTagName("team1Score").item(0).getTextContent();
					String team2 =      gameElement.getElementsByTagName("team2").item(0).getTextContent();
					String team2Score = gameElement.getElementsByTagName("team2Score").item(0).getTextContent();
					String date = 	  gameElement.getElementsByTagName("date").item(0).getTextContent();
					games.add(new Game(team1, Integer.parseInt(team1Score), team2, Integer.parseInt(team2Score), date));
				}
			}
		} 
		catch (SAXParseException err) {
			System.out.println ("** Parsing error" + ", line " 
					+ err.getLineNumber () + ", uri " + err.getSystemId ());
			System.out.println(" " + err.getMessage ());
		} 
		catch (SAXException e) {
			Exception x = e.getException ();
			((x == null) ? e : x).printStackTrace ();
		} 
		catch (Throwable t) {
			t.printStackTrace ();
		}
	}

	/*
	 * For debugging purposes, this function can dump the teams and games
	 */
	public static void dumpGames() {
		for (String team: teams.keySet()) {				// for each team
			System.out.println(team + " " + teams.get(team).tLabel );
			Vector<Game> teamGames = teams.get(team).games;

			java.util.Enumeration e = teamGames.elements();		
			while (e.hasMoreElements()) {				// for each game
				Game g = (Game) e.nextElement();
				System.out.print  ("  " + g.team1.name + " " + g.team1score);
				System.out.print  (" "  + g.team2.name + " " + g.team2score);
				System.out.println(" -- " + g.date + "---" + g.label);
			}
		}
	}

	/*
	 * main loads the games, creates a BB object, and makes it visible.
	 */
	public static void main( String args[] ) {
		JFrame f = new BB();
		f.setBounds(50,50,800,600);
		f.setVisible(true);

		f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		f.addWindowListener(new WindowAdapter() {
			public void windowClosed(WindowEvent e) {
				System.exit(0);
			}
		});

	}	/* end of main() */
}	/* end of class BB */

